package biolabs.mcl

import scala.util.parsing.combinator.JavaTokenParsers

/** An expression, for example an initialization argument of a supertype.
  * We recognize two types of expressions:
  *  - a child selector, which is of the form `children` or `children(p, q)`,
  *    where `children` is the name o a group of model's children and `p` and
  *    `q` are names of values associated with these children;
  *  - a verbatim expression, which is not parsed by the MCL interpreter and
  *    should represent a valid expression in the target language (Scala).
  */
abstract class Expr

object Expr extends JavaTokenParsers {

	val childSelectorCandidate: Parser[(String, List[String])] =
		ident ~ opt("(" ~> rep1sep(ident, ",") <~ ")") ^^ {
			case ~(id, Some(list)) => (id, list)
			case ~(id, None) => (id, Nil)
		}

	def analyze(arg: String, conts: List[Containment], model: ModelDefinition): AnalysisResult[Expr] =
		parseAll(childSelectorCandidate, arg) match {
			case suc: Success[(String, List[String])] =>
				val (name, assoc) = suc.get
				conts find (_.name == name) match {
					case Some(cont) => cont match {
						case SingletonContainment(_, _, _, _) =>
							if(assoc.isEmpty)
								AnalysisSuccess(ChildSelectorExpr(model, cont, Nil))
							else
								AnalysisError(assoc map { _ + " does not name a value associated with " + name } mkString "\n")
						case mc: MultiContainment =>
							if(assoc.forall(mc.associates(_)))
								AnalysisSuccess(ChildSelectorExpr(model, mc, mc.associatedValueNamesToNumbers(assoc)))
							else {
								val wrong = assoc filterNot (mc.associates(_))
								AnalysisError(wrong map { _ + " does not name a value associated with " + name } mkString "\n")
							}
					}
					case None =>
						AnalysisSuccess(VerbatimExpr(arg))
				}
			case err: NoSuccess =>
				AnalysisSuccess(VerbatimExpr(arg))
		}
}

/** An expression that was recognized as referring to a group of children,
  * optionally with some associated values. A sample occurrence in MCL as
  * an initialization argument of a super type:
  * {{{
  * model M
  *   combines
  *     N(children(c, a))
  *   contains
  *     children: X... associated with (a: A, b: B, c: C)
  * }}}
  * The super type `N` is initialized with a child selector for `children`
  * adapted in a way that it selects children of type `X` associated with
  * a pair of values of type `(C, A)`, i.e. the initialization argument
  * of `N` is of type `AssociativeChildSelector[M, X, (C, A)]`.
  */
case class ChildSelectorExpr(model: ModelDefinition, children: Containment, variant: List[Int]) extends Expr

/** An expression that is a verbatim copy of the input and is not parsed
  * by the MCL interpreter and should represent a valid expression in
  * the target language (Scala).
  */
case class VerbatimExpr(expr: String) extends Expr